﻿using System;
using System.Globalization;
using System.Security.Principal;
using System.Web.Mvc;
using System.Web.Security;
using AspNetMvc.Providers;
using AspNetMvc.Services.Authentication;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.RelyingParty;
using MvcContrib;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using WebGuiMvc.Models;
using UsersProvider.Repositories.RelationalRepositories;

namespace AspNetMvc.Controllers
{
   public partial class AccountController : BaseController
   {
      private static ProfileModel profile;
      private static OpenIdRelyingParty openid = new OpenIdRelyingParty();

      public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
      {
         FormsAuth = formsAuth ?? new FormsAuthenticationService();
         MembershipService = service ?? new AccountMembershipService(null);
      }

      public IFormsAuthentication FormsAuth
      {
         get;
         private set;
      }

      public IMembershipService MembershipService
      {
         get;
         private set;
      }

      [ValidateInput(false)]
      public virtual ActionResult OpenIdAuthenticate(string returnUrl)
      {
         var response = openid.GetResponse();
         if (response == null)
         {
            // Stage 2: user submitting Identifier
            Identifier id;
            if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
            {
               try
               {
                  var req = openid.CreateRequest(Request.Form["openid_identifier"]);
                  req.AddExtension(new ClaimsRequest
                  {
                     Nickname = DemandLevel.Request,
                     Email = DemandLevel.Request,
                     //FullName = DemandLevel.Request,
                     //Country = DemandLevel.Request,
                     //PostalCode = DemandLevel.Request,
                     //TimeZone = DemandLevel.Request,
                  });

                  return req.RedirectingResponse.AsActionResult();
               }
               catch (ProtocolException ex)
               {
                  Session["ErrorMessage"] = ex.Message;
                  return View((string) Views.LogOn);
               }
            }
            else
            {
               Session["ErrorMessage"] = ViewStrings.LogOnStrings.InvalidOpenId;
               return View((string) Views.LogOn);
            }
         }
         else
         {
            // Stage 3: OpenID Provider sending assertion response
            switch (response.Status)
            {
               case AuthenticationStatus.Authenticated:
                  ClaimsResponse claims = response.GetExtension<ClaimsResponse>();
                  OpenIdUserInfo info = claims != null ? new OpenIdUserInfo(claims.Email, claims.Nickname) : null;
                  Session["OpenIdInfo"] = info;
                  string openIdUrl = response.ClaimedIdentifier;

                  string userName = MembershipService.GetUsernameFromOpenId(openIdUrl, info);

                  if (string.IsNullOrEmpty(userName))
                  {
                     ViewData["ReturnUrl"] = returnUrl;
                     ViewData["OpenIdUrl"] = openIdUrl;
                     return View((string) Views.UsernameCreated);
                     //var status = MembershipService.CreateUser(openIdUrl, info, out userName);
                     //if (status != MembershipCreateStatus.Success)
                     //{
                     //   Session["ErrorMessage"] = ErrorCodeToString(status);
                     //   return View(Views.LogOn);
                     //}
                  }

                  Session["FriendlyIdentifier"] = userName;
                  FormsAuth.SignIn(userName, false);
                  Session["Profile"] = new ProfileModel(new UsersRepository().GetUserByLogin(userName));
                  if (!String.IsNullOrEmpty(returnUrl))
                  {
                     ViewData["ReturnUrl"] = returnUrl;
                     return Redirect(returnUrl);
                  }

                  return this.RedirectToRoute("Root");

               case AuthenticationStatus.Canceled:
                  Session["ErrorMessage"] = ViewStrings.LogOnStrings.OpenIdProviderCanceled;
                  return View((string) Views.LogOn);
               case AuthenticationStatus.Failed:
                  Session["ErrorMessage"] = response.Exception.Message;
                  return View((string) Views.LogOn);
            }
         }
         return new EmptyResult();
      }

      public virtual ActionResult CreateUser(string username, string returnUrl, string openIdUrl)
      {
         IOpenIdUserInfo info = Session["OpenIdInfo"] as IOpenIdUserInfo;
         //string generatedUsername = claims != null && claims.MailAddress != null ? claims.MailAddress.User : "User";

         if (ValidateUsername(username))
         {
            var status = MembershipService.CreateUser(username, openIdUrl, Language, info);
            if (status != MembershipCreateStatus.Success)
            {
               Session["ErrorMessage"] = ViewStrings.LogOnStrings.OpenIdAssocFailed;
               return View((string) Views.LogOn);
            }

            FormsAuth.SignIn(username, false);

            if (!string.IsNullOrEmpty(returnUrl)) this.Redirect(returnUrl);

            return this.RedirectToAction(MVC.Search.Index());
         }

         ViewData["ReturnUrl"] = returnUrl;
         ViewData["OpenIdUrl"] = openIdUrl;
         return View((string) Views.UsernameCreated);
         //Session["FriendlyIdentifier"] = generatedUsername;
         //FormsAuthentication.SetAuthCookie(generatedUsername, false);
         //if (!String.IsNullOrEmpty(returnUrl))
         //{
         //    ViewData["ReturnUrl"] = returnUrl;
         //    return View(Views.UpdateProfile);
         //}

         //return this.RedirectToAction(e => e.UpdateProfile());
      }

      [AcceptVerbs(HttpVerbs.Get)]
      [Authorize]
      public virtual ActionResult UpdateProfile()
      {
         profile = profile ?? Session["Profile"] as ProfileModel;
         ViewData.Model = profile;
         return View();
         //return new EmptyResult();
      }

      public virtual ActionResult UpdateProfile(FormCollection collection)
      {
         UpdateModel(profile);
         new UsersRepository().SetUserLanguage(User.Identity.Name, profile.Language);

         ViewData.Model = profile;
         return RedirectToAction(MVC.Account.ActionNames.UpdateProfile);
      }

      public virtual ActionResult LogOn()
      {
         Session["ErrorMessage"] = null;
         ModelState.Clear();
         return View();
      }

      [AcceptVerbs(HttpVerbs.Post)]
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
          Justification = "Needs to take same parameter type as Controller.Redirect()")]
      public virtual ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl)
      {
         if (!ValidateLogOn(userName, password))
         {
            return View();
         }

         FormsAuth.SignIn(userName, rememberMe);
         Session["Profile"] = new ProfileModel(new UsersRepository().GetUserByLogin(userName));
         if (!String.IsNullOrEmpty(returnUrl))
         {
            return Redirect(returnUrl);
         }
         else
         {
            return this.RedirectToAction(MVC.Search.ActionNames.Index);
         }
      }

      public virtual ActionResult LogOff()
      {
         FormsAuth.SignOut();
         return this.RedirectToAction(MVC.Search.ActionNames.Index, MVC.Search.Name);
      }

      public virtual ActionResult Confirm(string confirmationHash)
      {
         var user = MembershipService.ApproveUser(confirmationHash);
         if (user != null)
         {
            ViewData["ReturnUrl"] = string.Format("/{0}/{1}",MVC.Account.Name, MVC.Account.ActionNames.UpdateProfile);
            ViewData["Msg"] = ViewStrings.LogOnStrings.AccountConfirmed;
            FormsAuth.SignIn(user.UserName, false);
            Session["Profile"] = new ProfileModel(new UsersRepository().GetUserByLogin(user.UserName));
         }
         else
         {
            ViewData["ReturnUrl"] = "/";
            ViewData["Msg"] = ViewStrings.LogOnStrings.HashExpired;
         }
         //ViewData["Message"] = "Confirmation was not successful";
         //UsersRepository repo = new UsersRepository();
         //var tempUser = repo.GetTempUsernameFromConfirmHash(confirmationHash);

         //if (tempUser.ConfirmTill > DateTime.Now)
         //{
         //   // Attempt to register the user
         //   MembershipCreateStatus createStatus = MembershipService.CreateUser(tempUser.Username, tempUser.Password, tempUser.Email, null);

         //   if (createStatus == MembershipCreateStatus.Success)
         //   {
         //      FormsAuth.SignIn(tempUser.Username, false /* createPersistentCookie */);
         //      //if (!string.IsNullOrEmpty(returnUrl))
         //      //{
         //      //   return Redirect(returnUrl);
         //      //}

         //      ViewData["Message"] = "Confirmation was successful";
         //      //return RedirectToAction("index", "search");
         //   }
         //   //else
         //   //{
         //   //   ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
         //   //}
         //}
         //else
         //{
         //   ViewData["Message"] = "Your confirmation link seems to be expired";
         //}
         return View();
      }

      public virtual ActionResult Register(string returnUrl)
      {
         ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
         return View();
      }

      [AcceptVerbs(HttpVerbs.Post)]
      public virtual ActionResult Register(string userName, string email, string language, string password, string confirmPassword, string returnUrl)
      {

         //ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

         if (ValidateRegistration(userName, email, password, confirmPassword))
         {
            var confirmationLink = "<a href=\"http://beaware.at/account/confirm?confirmationHash={0}\">http://beaware.at/account/confirm?confirmationHash={0}</a>";
            confirmationLink = string.Format(ViewStrings.LogOnStrings.ConfirmationHtml, confirmationLink);
            var status = MembershipService.CreateUser(userName, password, email, Language, confirmationLink, null);
            if (status == MembershipCreateStatus.Success)
            {
               new UsersRepository().SetUserLanguage(userName, language);
               ViewData["ReturnUrl"] = "/";//string.Format("/{0}/{1}", MVC.Search.Name, MVC.Search.ActionNames.Index);
               ViewData["Msg"] = ViewStrings.LogOnStrings.ConfirmationLinkSent;
               return View((string) Views.Confirm);
            }
            else ModelState.AddModelError("_FORM", ErrorCodeToString(status));
         }

         //ModelState.AddModelError("_FORM", "");
         // If we got this far, something failed, redisplay form
         return View();
      }

      void client_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
      {
         
      }

      [Authorize]
      public virtual ActionResult ChangePassword()
      {

         ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

         return View();
      }

      [Authorize]
      [AcceptVerbs(HttpVerbs.Post)]
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
          Justification = "Exceptions result in password not being changed.")]
      public virtual ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword)
      {

         ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

         if (!ValidateChangePassword(currentPassword, newPassword, confirmPassword))
         {
            return View();
         }

         try
         {
            if (MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword))
            {
               return RedirectToAction("ChangePasswordSuccess");
            }
            else
            {
               ModelState.AddModelError("_FORM", ViewStrings.LogOnStrings.PasswordError);
               return View();
            }
         }
         catch
         {
            ModelState.AddModelError("_FORM", ViewStrings.LogOnStrings.PasswordError);
            return View();
         }
      }

      public virtual ActionResult ChangePasswordSuccess()
      {
         return View();
      }

      protected override void OnActionExecuting(ActionExecutingContext filterContext)
      {
         if (filterContext.HttpContext.User.Identity is WindowsIdentity)
         {
            throw new InvalidOperationException("Windows authentication is not supported.");
         }
      }

      public virtual ActionResult ChangeCulture(string lang, string returnUrl)
      {
         Session["Culture"] = new CultureInfo(lang);
         return Redirect(returnUrl);
      }

      #region Validation Methods
      private bool ValidateUsername(string username)
      {
         if (new UsersRepository().LoginExists(username))
         {
            ModelState.AddModelError("username", ViewStrings.LogOnStrings.UsernameExists);
         }

         return ModelState.IsValid;
      }


      private bool ValidateChangePassword(string currentPassword, string newPassword, string confirmPassword)
      {
         if (String.IsNullOrEmpty(currentPassword))
         {
            ModelState.AddModelError("currentPassword", ViewStrings.LogOnStrings.PasswordRequired);
         }
         if (newPassword == null || newPassword.Length < MembershipService.MinPasswordLength)
         {
            ModelState.AddModelError("newPassword",
                String.Format(CultureInfo.CurrentCulture,
                     ViewStrings.LogOnStrings.PasswordLengthRequired,
                     MembershipService.MinPasswordLength));
         }

         if (!String.Equals(newPassword, confirmPassword, StringComparison.Ordinal))
         {
            ModelState.AddModelError("_FORM", ViewStrings.LogOnStrings.PassConfirmationMatchFailed);
         }

         return ModelState.IsValid;
      }

      private bool ValidateLogOn(string userName, string password)
      {
         if (String.IsNullOrEmpty(userName))
         {
            ModelState.AddModelError("username", ViewStrings.LogOnStrings.UsernameRequired);
         }
         if (String.IsNullOrEmpty(password))
         {
            ModelState.AddModelError("password", ViewStrings.LogOnStrings.PasswordRequired);
         }
         if (!MembershipService.ValidateUser(userName, password))
         {
            ModelState.AddModelError("_FORM", ViewStrings.LogOnStrings.UserOrPassInvalid);
         }

         return ModelState.IsValid;
      }

      private bool ValidateOpenIdRegistration(string openIdURL, IOpenIdUserInfo info)
      {
         if (string.IsNullOrEmpty(openIdURL))
         {
            ModelState.AddModelError("_FORM", ViewStrings.LogOnStrings.InvalidOpenId);
         }
         if (!MembershipService.ValidateUser(openIdURL, info))
         {
            ModelState.AddModelError("_FORM", ViewStrings.LogOnStrings.OpenIdExists);
         }

         return ModelState.IsValid;
      }

      private bool ValidateRegistration(string userName, string email, string password, string confirmPassword)
      {
         if (String.IsNullOrEmpty(userName))
         {
            ModelState.AddModelError("username", ViewStrings.LogOnStrings.UsernameRequired);
         }
         if (String.IsNullOrEmpty(email))
         {
            ModelState.AddModelError("email", ViewStrings.LogOnStrings.EmailRequired);
         }
         if (password == null || password.Length < MembershipService.MinPasswordLength)
         {
            ModelState.AddModelError("password",
                String.Format(CultureInfo.CurrentCulture,
                     ViewStrings.LogOnStrings.PasswordLengthRequired,
                     MembershipService.MinPasswordLength));
         }
         if (!String.Equals(password, confirmPassword, StringComparison.Ordinal))
         {
            ModelState.AddModelError("_FORM", ViewStrings.LogOnStrings.PassConfirmationMatchFailed);
         }
         return ModelState.IsValid;
      }

      private static string ErrorCodeToString(MembershipCreateStatus createStatus)
      {
         // See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for
         // a full list of status codes.
         switch (createStatus)
         {
            case MembershipCreateStatus.DuplicateUserName:
               return ViewStrings.LogOnStrings.DuplicateUserName;

            case MembershipCreateStatus.DuplicateEmail:
               return ViewStrings.LogOnStrings.DuplicateEmail;

            case MembershipCreateStatus.InvalidPassword:
               return ViewStrings.LogOnStrings.InvalidPassword;

            case MembershipCreateStatus.InvalidEmail:
               return ViewStrings.LogOnStrings.InvalidEmail;

            case MembershipCreateStatus.InvalidAnswer:
               return ViewStrings.LogOnStrings.InvalidAnswer;

            case MembershipCreateStatus.InvalidQuestion:
               return ViewStrings.LogOnStrings.InvalidQuestion;

            case MembershipCreateStatus.InvalidUserName:
               return ViewStrings.LogOnStrings.InvalidUserName;

            case MembershipCreateStatus.ProviderError:
               return ViewStrings.LogOnStrings.ProviderError;

            case MembershipCreateStatus.UserRejected:
               return ViewStrings.LogOnStrings.UserRejected;

            default:
               return ViewStrings.LogOnStrings.UnknownError;
         }
      }
      #endregion
   }
}
